package com.wuxianedu.loadimage;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.widget.ImageView;

import com.wuxianedu.loadimage.DownloadImgUtils;
import com.wuxianedu.loadimage.ImageSizeUtil;
import com.wuxianedu.loadimage.MD5Utils;

import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * 图片加载类
 * Created by sks on 2016/4/2.
 */
public class ImageLoader {

    private static ImageLoader mInstance;

    //线程池
    private ExecutorService mThreadPool;

    //任务队列
    private LinkedList<Runnable> mTaskQueue;

    //图片缓存之内存缓存技术LruCache，当存储Image的大小大于LruCache设定的值，系统自动释放内存
    private LruCache<String, Bitmap> mLruCache;

    //队列的调度方式
    private Type mType = Type.LIFO;

    //信号量
    private Semaphore mSemaphorePoolThreadHandler = new Semaphore(0);
    private Semaphore mSemaphoreThreadPool;

    //后台轮询线程
    private Thread mPoolThread;
    private Handler mPoolThreadHandler;

    //UI线程中的Handler
    private Handler mUIHandler;

    //是否开启本地SD卡缓存
    private boolean isDiskCacheEnable = true;

    private ImageLoader(int threadCount, Type type) {
        init(threadCount, type);
    }

    public static ImageLoader getInstance(int threadCount, Type type) {
        if (mInstance == null) {
            synchronized (ImageLoader.class) {
                if (mInstance == null) {
                    mInstance = new ImageLoader(threadCount, type);
                }
            }
        }
        return mInstance;
    }

    //加载策略
    public enum Type {
        FIFO, //先进先出
        LIFO; //后进先出
    }

    /**
     * 初始化
     * @param threadCount
     * @param type
     */
    private void init(int threadCount, Type type){
        initBackThread();
        // 获取我们应用的最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheMemory = maxMemory/8;
        mLruCache = new LruCache<String,Bitmap>(cacheMemory){

            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight();
            }

        };

        // 创建线程池
        mThreadPool = Executors.newFixedThreadPool(threadCount);
        mTaskQueue = new LinkedList<Runnable>();
        mType = type;
        mSemaphoreThreadPool = new Semaphore(threadCount);
    }

    /**
     * 初始化后台线程
     */
    private void initBackThread(){
        mPoolThread = new Thread(){

            @Override
            public void run() {
                Looper.prepare();

                mPoolThreadHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        //线程池取出一个任务执行
                        mThreadPool.execute(getTask());
                        try {
                            mSemaphoreThreadPool.acquire();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };
                //释放信号量
                mSemaphorePoolThreadHandler.release();
                Looper.loop();
                super.run();
            }
        };

        mPoolThread.start();
    }

    /**
     * 从任务队列取出任务
     * @return
     */
    private Runnable getTask(){
        if (mType == Type.FIFO) {
            return mTaskQueue.removeFirst();
        } else if (mType == Type.LIFO) {
            return mTaskQueue.removeLast();
        }
        return null;
    }


    /**
     * 从本地加载图片
     * @param path
     * @param imageView
     * @return
     */
    private Bitmap loadImageFromLocal(final String path,final ImageView imageView) {
        // 1、获得图片需要显示的大小
        ImageSizeUtil.ImageSize imageSize = ImageSizeUtil.getImageViewSize(imageView);
        // 2、压缩图片
        return decodeSampledBitmapFromPath(path, imageSize.width,imageSize.height);
    }

    /**
     * 根据图片需要显示的宽和高对图片进行压缩
     * @param path
     * @param width
     * @param height
     * @return
     */
    protected Bitmap decodeSampledBitmapFromPath(String path, int width, int height) {
        // 获得图片的宽和高，并不把图片加载到内存中
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        //如果inSampleSize为2，则宽和高同时缩放1/2，比如1024*1024的图片，如果inSampleSize为2,那么宽和高
        //变为512*512，实际图片大小变为原来4分之一
        options.inSampleSize = ImageSizeUtil.caculateInSampleSize(options, width, height);
        // 使用获得到的InSampleSize再次解析图片
        options.inJustDecodeBounds = false;
        Bitmap bitmap = BitmapFactory.decodeFile(path, options);
        return bitmap;
    }

    /**
     * 加载图片
     * @param path 图片url
     * @param imageView
     * @param isFromNet 是否从网络加载
     */
    public void loadImage(final String path, final ImageView imageView, final boolean isFromNet) {
        imageView.setTag(path);
        if (mUIHandler == null) {
            mUIHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // 获取得到图片，为imageview回调设置图片
                    ImgBeanHolder holder = (ImgBeanHolder) msg.obj;
                    Bitmap bm = holder.bitmap;
                    ImageView imageview = holder.imageView;
                    String path = holder.path;
                    // 将path与getTag存储路径进行比较,处理图片错乱问题
                    if (imageview.getTag().toString().equals(path)) {
                        imageview.setImageBitmap(bm);
                    }
                }
            };
        }

        // 根据path在缓存中获取bitmap
        Bitmap bm = getBitmapFromLruCache(path);
        //从内存中取出图片，内存有图片，直接显示到ImageView中
        if(bm!=null){
            refreashBitmap(path,imageView,bm);
        }else{
            addTask(buildTask(path, imageView, isFromNet));
        }
    }

    private Runnable buildTask(final String path, final ImageView imageView, final boolean isFromNet) {
        return new Runnable() {
            @Override
            public void run() {
                Bitmap bm = null;
                if (isFromNet) {
                    //先从本地加载，本地不存在再从网络加载
                    File file = getDiskCacheDir(imageView.getContext(), MD5Utils.md5(path));
                    if(file.exists()){ //判断图片在本地缓存中是否存在
                        bm = loadImageFromLocal(file.getAbsolutePath(),imageView);
                    }else{
                        //从网络加载
                        if(isDiskCacheEnable){
                            //下载图片
                            boolean downloadState = DownloadImgUtils.downloadImgByUrl(path,file);
                            if(downloadState){
                                bm = loadImageFromLocal(file.getAbsolutePath(),imageView);
                            }else{
                                //直接从网络加载
                                bm = DownloadImgUtils.downloadImgByUrl(path, imageView);
                            }
                        } else{// 直接从网络加载
                            bm = DownloadImgUtils.downloadImgByUrl(path, imageView);
                        }
                    }
                }else{
                    //从本地加载
                    bm = loadImageFromLocal(path, imageView);
                }

                // 3、把图片加入到缓存
                addBitmapToLruCache(path, bm);
                refreashBitmap(path, imageView, bm);
                mSemaphoreThreadPool.release();
            }
        };
    }

    /**
     * 添加任务
     * @param runnable
     */
    private synchronized void addTask(Runnable runnable) {
        mTaskQueue.add(runnable);
        try {
            if (mPoolThreadHandler == null) {
                mSemaphorePoolThreadHandler.acquire();
            }
        } catch (InterruptedException e) {
        }
        mPoolThreadHandler.sendEmptyMessage(0x110);
    }

    /**
     * 将图片显示到ImageView
     * @param path
     * @param imageView
     * @param bm
     */
    private void refreashBitmap(final String path, final ImageView imageView, Bitmap bm) {
        Message message = Message.obtain();
        ImgBeanHolder holder = new ImgBeanHolder();
        holder.bitmap = bm;
        holder.path = path;
        holder.imageView = imageView;
        message.obj = holder;
        mUIHandler.sendMessage(message);
    }

    /**
     * 将图片加入LruCache
     * @param path
     * @param bm
     */
    protected void addBitmapToLruCache(String path, Bitmap bm) {
        if (getBitmapFromLruCache(path) == null) {
            if (bm != null){
                mLruCache.put(path, bm);
            }
        }
    }

    /**
     * 从LruCache中获取bitmap
     * @param key
     * @return
     */
    private Bitmap getBitmapFromLruCache(String key) {
        return mLruCache.get(key);
    }

    /**
     * 获得缓存图片的地址
     * @param context
     * @param uniqueName
     * @return
     */
    public File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }

    private class ImgBeanHolder {
        Bitmap bitmap;
        ImageView imageView;
        String path;
    }

}
